#include <dirent.h>
#include <stdio.h>
#include <string.h>
#include "heap.h"
#include "dir_explorer.h"


#define DOT "."
#define DOT_DOT ".."
#define TAM 20

struct filtro_archivos{
	char** extensiones;
	int max;
	int cant;
};


/* ******************************************************************
 *                    	IMPLEMENTACION
 * *****************************************************************/


/* Extrae la extension de un archivo recibido como parametro */
char* archivo_extraer_extension(char* archivo){

	cola_t* cola = cola_crear();

	int len = 0;

	bool en_ext = false;

	for (int i = 0 ; i < strlen(archivo) ; i++ ){

		if (archivo[i] != '.' && !en_ext)
			continue;

		if (archivo[i] == '.'){
			en_ext = true;
			while (!cola_esta_vacia(cola))
				cola_desencolar(cola);
			len = 0;
			continue;
		}

		cola_encolar(cola, &archivo[i]);
		len++;

	}

	char* extension = malloc((len+1)*sizeof(char));

	if (!extension)
		return NULL;

	int i = 0;
	while (!cola_esta_vacia(cola)){
		char* letra = cola_desencolar(cola);
		extension[i] = *letra;
		i++;
	}

	extension[i] = '\0';

	cola_destruir(cola,NULL);

	return extension;

}


char* componer_ruta(const char* dir, const char* arch){

	int len_dir = strlen(dir);
	int len_arch = strlen(arch);
	bool bar = false;

	if (dir[len_dir-1] == '/')
		bar = true;

	int len = len_arch + len_dir + 1;

	if (!bar){
		len += 1;
	}

	char *ruta = malloc (len*sizeof(char));
	if (!ruta)
		return NULL;

	strcpy(ruta,dir);
	if (!bar)
		strcat(ruta,"/");
	strcat(ruta,arch);
	strcat(ruta,"\0");



	return ruta;



}


/* Crea un filtro de archivos, sin extensiones a filtrar. En caso de error devuelve NULL */
filtro_archivos_t* filtro_archivos_crear(){

	filtro_archivos_t* filtro = malloc(sizeof(filtro_archivos_t));

	if (!filtro)
		return NULL;

	filtro->max = 5;

	filtro->extensiones = malloc( (filtro->max)* sizeof(char*) );

	if (!filtro->extensiones){
		free (filtro);
		return NULL;
	}


	filtro->cant = 0;

	return filtro;

}

/* Destruye el filtro recibido como parametro */
void filtro_archivos_destruir(filtro_archivos_t* filtro){

	for (int i = 0 ; i < filtro->cant ; i++)
		free(filtro->extensiones[i]);

	free(filtro->extensiones);

	free(filtro);

}

/* Agrega la extension pasada como parametro.
 * La extension debe ser pasada sin el punto (.).
 * ex: filtro_archivos_agregar_extension(filtro, "txt") */
bool filtro_archivos_agregar_extension(filtro_archivos_t* filtro, char* extension){

	if (filtro->cant >= filtro->max){

		char** n_ext = realloc(filtro->extensiones, (filtro->max + 5)* sizeof(char*) );

		if (!n_ext){
			return false;
		}

		filtro->extensiones = n_ext;
		filtro->max = filtro->max + 5;

	}

	filtro->extensiones[filtro->cant] = malloc((strlen(extension)+1)*sizeof(char));

	if (!filtro->extensiones[filtro->cant])
		return false;

	strcpy(filtro->extensiones[filtro->cant],extension);


	filtro->cant++;



	return true;


}

/* Devuelve una copia de las extensiones agregadas al filtro y
 * la cantidad en la variable pasada como parametro */
char** filtro_archivos_obtener_extensiones(filtro_archivos_t* filtro, int* cant){

	char** copia_exts = malloc(filtro->cant*sizeof(char*));

	if (!copia_exts)
		return NULL;

	for (int i = 0 ; i < filtro->cant; i++){

		int len = strlen(filtro->extensiones[i]);
		copia_exts[i] = malloc((len+1)*sizeof(char));

		if (!copia_exts[i]){
			for (int j = 0; j < i ; j++ )
				free(copia_exts[j]);
			free(copia_exts);
			return NULL;
		}

		strcpy(copia_exts[i],filtro->extensiones[i]);

	}

	*cant = filtro->cant;

	return copia_exts;


}

/* Verifica si la extension pasada como parametro esta agregada en el filtro  y devuelve
 * true o false segun sea el caso */
bool filtro_archivos_extension_compatible(filtro_archivos_t* filtro, char* extension){

	bool compatibilidad = false;

	for (int i = 0; i < filtro->cant ; i++){

		if (strcmp(extension,filtro->extensiones[i]) == 0){

			compatibilidad = true;
			break;

		}

	}

	return compatibilidad;

}


/* Filtra el vector de archivos recibido, devolviendo un nuevo vector con los archivos
 * de las extensiones agregadas. Se recibe en el parametro cant la cantidad de archivos
 * en el vector original y (¡¡WARNING!!) el valor es sobrescrito con la cantidad de
 * archivos en el vector filtrado. En caso de error se devuelve NULL */
vector_t* filtro_archivos_filtrar(filtro_archivos_t* filtro, vector_t* archivos, int* cant){

	vector_t* archivos_filtrados = vector_crear(vector_obtener_tamanio(archivos));

	if (!archivos_filtrados)
		return NULL;

	int cantidad = *cant;
	int indice= 0;

	for (int i = 0; i < cantidad; i++){

		char* archivo = vector_obtener(archivos, i);

		char* extension = archivo_extraer_extension(archivo);

		if (filtro_archivos_extension_compatible(filtro,extension)){

			free (extension);

			char* filtrado = malloc((strlen(archivo)+1)*sizeof(char));

			strcpy(filtrado, archivo);

			vector_guardar(archivos_filtrados,indice,filtrado);
			indice++;

			continue;

		}

		free (extension);


	}

	vector_destruir_con_elementos(archivos,free,*cant);

	*cant = indice;

	return archivos_filtrados;

}

int inv_strcmp(const char* char1, const char* char2){

	if (strcmp(char1,char2) > 0)
		return -1;
	if (strcmp(char1,char2) < 0)
		return 1;

	return 0;

}


/* Crea un vector con todos los archivos y directorios contenidos en el directorio
 * recibido como paramtero  */
vector_t* dir_obtener_archivos(const char* directorio, int* cant_archivos){

	struct dirent* file;

	*cant_archivos = 0;

	cmp_func_t cmp = (cmp_func_t) inv_strcmp;

	heap_t* heap = heap_crear(cmp);

	DIR* dir = opendir(directorio);

	if (!dir){
		heap_destruir(heap,NULL);
		return NULL;
	}

	while ( ( file = readdir ( dir ) ) ){

		if ( ( strcmp(file->d_name,DOT) == 0 ) || ( strcmp(file->d_name,DOT_DOT) == 0 )  )
			continue;

		char* nombre = componer_ruta(directorio, file->d_name);
		if (nombre)
			heap_encolar(heap,nombre);
	
	}

	*cant_archivos = heap_cantidad(heap);

	vector_t *vector = heap_convertir_a_vector(heap);

	heap_destruir(heap,NULL);

	closedir(dir);

	return vector;

}

